package edu.maping.easy.sequence;

import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.bind.annotation.XmlElement;

import napplet.EMNit;
import napplet.InterfazPlayPause;

import processing.core.PApplet;
import processing.core.PFont;
import processing.xml.XMLElement;
import controlP5.Button;
import controlP5.ControlEvent;
import controlP5.ControlP5;
import controlP5.ScrollList;
import controlP5.Textfield;
import edu.maping.easy.ControlFacade;
import edu.maping.easy.gui.ButtonList;
import edu.maping.easy.gui.GUIEvent;
import edu.maping.easy.gui.GuiFactory;
import edu.maping.easy.gui.ScreenLogger;
import edu.maping.easy.gui.Slider;
import edu.maping.easy.io.KeyBoardInteraction;
import edu.maping.easy.model.ControlP5Interaction;
import edu.maping.easy.model.EasyMappingPlugin;
import edu.maping.easy.model.GUIInteraction;
import edu.maping.easy.model.MouseInteraction;
import edu.maping.easy.model.Point;
import edu.maping.easy.model.PolygonThing;
import edu.maping.easy.model.Thing;
import edu.maping.easy.model.World;
import edu.maping.easy.painter.PainterDelegateEditor;
import edu.maping.easy.painter.PolygonRendererManager;
import edu.maping.easy.painter.ThingPainterDelegate;
import edu.maping.easy.painter.plugins.TimeRunner;
import edu.maping.easy.sequence.ticker.TickerListener;

public class Sequencer implements MouseInteraction, EasyMappingPlugin,
		EasyMappingTimeTicker, GUIInteraction {

	private PApplet pApplet = null;

	private World world = null;

	private boolean doing = false;

	boolean started = false;

	private SequenceStep currentActionEvent = null;

	private ArrayListStepper<SequenceStep> curr = null;

	private PFont fontA;

	private SequenceRenderer sequenceRenderer = new SequenceRenderer();

	private edu.maping.easy.gui.Button left;
	private edu.maping.easy.gui.Button rigth;
	private ScrollList scrollList;
	private Textfield textfield;
	edu.maping.easy.gui.Button newSequence;

	private String NAME_NEW_SEQUENCE = "New Sequence";
	private String NAME_NEW_SEQUENCE_LIST = "ScrolSequence";

	private ArrayList<ArrayListStepper<SequenceStep>> actionSequences = new ArrayList<ArrayListStepper<SequenceStep>>();

	Map<edu.maping.easy.gui.Button, ArrayListStepper<SequenceStep>> mappedButtons = new HashMap<edu.maping.easy.gui.Button, ArrayListStepper<SequenceStep>>();
	Map<String, edu.maping.easy.gui.Button> mappedActions = new HashMap<String, edu.maping.easy.gui.Button>();

	private PolygonThing physicPolyThing = null;
	public PolygonRendererManager rendererManager = null;

	public ScreenLogger screenLogger = null;

	int seqCounter = 0;

	public GuiFactory guiFactory;
	public PainterDelegateEditor painterDelegateEditor;

	ButtonList buttonList = null;

	public void editKeyPressed(int key, Thing thing) {

	}

	@Override
	public void controlEvent(GUIEvent theEvent) {

		if (theEvent.name().equals("Sequence")) {
			doing = !doing;
			if (doing) {
				left.setDrawable(true);
				rigth.setDrawable(true);
				newSequence.setDrawable(true);
				showSequenceList();
				painterDelegateEditor.createNewButtons(guiFactory);

			} else {
				left.setDrawable(false);
				rigth.setDrawable(false);
				newSequence.setDrawable(false);
				hideSequenceList();
				painterDelegateEditor.deleteOlderButtons(guiFactory);
			}
		}
		
		//
		if (doing) {
			
			if (theEvent.name().startsWith("seq-")) {
				curr = mappedButtons.get(theEvent.button);
				Iterator<edu.maping.easy.gui.Button> buttons = mappedButtons
						.keySet().iterator();
				while (buttons.hasNext()) {
					buttons.next().setFillColor(guiFactory.buttonColor);
				}
				theEvent.button.setFillColor(guiFactory.selectedColor);
				curr.next();
			}

			if (theEvent.name().equals("<--")) {
				curr.previous();
				currentActionEvent = curr.getCurrentElement();
			}

			if (theEvent.name().equals("-->")) {
				curr.next();
				currentActionEvent = curr.getCurrentElement();
			}

			if (theEvent.name().equals(NAME_NEW_SEQUENCE)) {
				addActionSequence("" + seqCounter++);
			}

			//
			rendererManager.thingActionEvent = currentActionEvent;
			ActionEvent actionEvent = rendererManager.controlEvent(theEvent);
			painterDelegateEditor.deleteOlderButtons(guiFactory);
			painterDelegateEditor.createNewButtons(guiFactory);
			if (actionEvent != null) {
				// MONTAMOS LA CONFIGURACION ASOCIADA A UN MOMENTO
				// actionEvent.thing = physicPolyThing;
				// Y LIBERAMOS
				if (actionEvent.actionConf == null) {
					// ESTO SIGNIFICA QUE HEMOS BORRADO, ASÃ� QUE...
					ActionEventList eventList = currentActionEvent.actionEventMap
							.get(physicPolyThing);
					eventList.remove(actionEvent);
					System.out.println("");
				}
				physicPolyThing = null;
			}
		}
	}

	@Override
	public boolean mouseClicked(int mouseX, int mouseY, Thing newThing,
			MouseEvent mouseEvent) {

		if (!doing)
			return false;

		if (mouseEvent.getButton() == MouseEvent.BUTTON3) {
			if (currentActionEvent != null) {
				addMoment();
			}
			return true;
		}

		if (currentActionEvent != null) {

			physicPolyThing = world.hovered(pApplet.mouseX, pApplet.mouseY);

			if (physicPolyThing != null) {

				ActionEventList actionEventList = currentActionEvent.actionEventMap
						.get(physicPolyThing);

				// SI TODAVï¿½ A NO EXISTE LA CREAMOS
				if (actionEventList == null) {
					actionEventList = new ActionEventList();
					currentActionEvent.actionEventMap.put(physicPolyThing,
							actionEventList);
				} else {
					// SI YA EXISTE QUEREMOS VISUALIZAR QUE PLUGIN TIENEN CONF
					// PARA PINTAR EN ESTE MOMENTO

				}
				// para no renderizar siempre la misma
				if (actionEventList != rendererManager.getActionEvents()) {
					// Point point = physicPolyThing.vertex.get(0);
					Point point = new Point(pApplet.width - 450, 20);
					rendererManager.render(point, actionEventList,
							physicPolyThing);
					return true;
				}
			}
		} else {
			screenLogger.writeText("CREATE A SEQUENCE FIRST");
		}

		return true;
	}

	private void addMoment() {
		currentActionEvent = new SequenceStep();
		curr.seqs.add(currentActionEvent);
		curr.previous();
		curr.previousPlayed();
		curr.init();

	}

	private void addToScrollList(String name,
			ArrayListStepper<SequenceStep> actionSequence, int id) {

		edu.maping.easy.gui.Button b = guiFactory.newButton("seq-" + name,
				buttonList.getX(), buttonList.getY() + 50, 50, 20);
		// b.setId(id);
		// b.setHeight(30);
		b.setGrabbable(true);
		b.setStatic(false);
		buttonList.add(b);
		mappedButtons.put(b, actionSequence);
		mappedActions.put(name, b);
	}

	private void addActionSequence(String name) {

		curr = new ArrayListStepper<SequenceStep>();
		addMoment();

		if (name == null) {
			textfield.setFocus(true);
			textfield.setColorBackground(pApplet.color(255, 0, 0));
			return;
		}

		curr.name = name;
		actionSequences.add(curr);
		addToScrollList(name, curr, actionSequences.size());
	}

	@Override
	public void mouseDragged(int mouseX, int mouseY, Thing newThing) {

	}

	@Override
	public void mousePreassed(int mouseX, int mouseY, Thing newThing) {

	}

	@Override
	public void mouseReleased(int mouseX, int mouseY, Thing newThing) {

	}

	@Override
	public void start(World world, PApplet pApplet, ControlP5 controlP5) {
		curr.restart();
		curr.init();
		started = true;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.maping.easy.sequence.EasyMappingTimeTicker#tick()
	 */
	public void tick() {

	}

	@Override
	public void KeyPressed(KeyBoardInteraction kBInteraction) {

	}

	public void setup(PApplet pApplet, World world,
			ControlFacade controlFacade, GuiFactory guiFactory) {
		this.pApplet = pApplet;
		this.world = world;
		this.guiFactory = guiFactory;

		fontA = pApplet.loadFont("Ziggurat-HTF-Black-32.vlw");
		pApplet.textFont(fontA, 24);

		ControlP5 controlP5 = (ControlP5) controlFacade;
		// saveSequence = controlP5.addButton(NAME_NEW_SEQUENCE_BUTTON, 0,
		// pApplet.width - 75, pApplet.height - 50, 80, 19);
		// saveSequence.setVisible(false);
		scrollList = controlP5.addScrollList(NAME_NEW_SEQUENCE_LIST,
				pApplet.width - 150, 40, 140, 280);
		scrollList.setItemHeight(30);
		scrollList.setBarHeight(20);
		scrollList.captionLabel().set("Sequences");
		scrollList.setVisible(false);

		edu.maping.easy.gui.Button startCompose = new edu.maping.easy.gui.Button(
				"Sequence", 250, 10, 70, 20, pApplet);
		guiFactory.add(startCompose);

		newSequence = guiFactory.newButton("New Sequence", pApplet.width - 150,
				pApplet.height - 70, 90, 20);
		newSequence.setDrawable(false);

		left = guiFactory.newButton("<--", pApplet.width - 150,
				pApplet.height - 25, 30, 20);
		left.setDrawable(false);
		rigth = guiFactory.newButton("-->", pApplet.width - 120,
				pApplet.height - 25, 30, 20);
		rigth.setDrawable(false);

		buttonList = guiFactory.newButtonList("Lista", pApplet.width - 150, 20,
				60, 20);
		buttonList.setDrawable(false);
		buttonList.setGrabbable(true);

	}

	public void draw(PApplet pApplet) {
		draw(pApplet, false);
	}

	public void nextStep(boolean editing) {

		if (editing) {
			curr.next();
		} else {
			curr.nextWithRotation();
		}
		// vamos a comprobar cuales son de ejecución continua para separarlos

		updateAfterMoveCurrElement();
	}

	public void previousStep(boolean editing) {

		if (editing) {
			curr.previous();
		} else {
			curr.previousPlayed();
		}
		
		updateAfterMoveCurrElement();
	}
	
	private void updateAfterMoveCurrElement() {
		stopLeaveSequenceStep();
		currentActionEvent = curr.getCurrentElement();
		addOrDeleteFromContinuePlaying();
	}

	private void stopLeaveSequenceStep() {
		Iterator<Thing> set = currentActionEvent.actionEventMap.keySet()
				.iterator();

		while (set.hasNext()) {
			Thing thing = set.next();
			ActionEventList actionEventList = currentActionEvent.actionEventMap
					.get(thing);
			for (ActionEvent actionEvent : actionEventList) {
				InterfazPlayPause emNit = (InterfazPlayPause) actionEvent.painterContainer.painterDelegate;
				emNit.pause();
			}
		}
	}

	private void addOrDeleteFromContinuePlaying() {

		Iterator<Thing> set = currentActionEvent.actionEventMap.keySet()
				.iterator();

		while (set.hasNext()) {
			Thing thing = set.next();
			ActionEventList actionEventList = currentActionEvent.actionEventMap
					.get(thing);
			for (ActionEvent actionEvent : actionEventList) {
				InterfazPlayPause emNit = (InterfazPlayPause) actionEvent.painterContainer.painterDelegate;
				
				emNit.play();
				
				if (actionEvent.actionConf
						.containsKey(PolygonRendererManager.OFF)) {
					emNit.pause();
				}

				// if (actionEvent.actionConf
				// .containsKey(PolygonRendererManager.ON)) {
				
				// }

			}
		}
	}

	public void draw(PApplet pApplet, boolean running) {

		if (doing) {
			sequenceRenderer.render(curr, pApplet);
			boolean editingPlugin = rendererManager.draw(pApplet);
			if (editingPlugin) {
				return;
			}
		}

		if (doing || running) {

			if (painterDelegateEditor != null && curr != null)
				painterDelegateEditor.setThingActionEvent(currentActionEvent);

			if (curr != null) {

				// if (pApplet.frameCount % 300 == 0) {
				// actionSequence.nextPlayedWithRotation();
				// painterDelegateEditor.setThingActionEvent(actionSequence.getCurrentElementPlayed());
				// }

				if (!running)
					painterDelegateEditor.draw(pApplet);

				// TODO optimizar laconfiguracion que se realiza una vez
				// del renderizado que se produce siempre

				SequenceStep thingActionEvent = curr.getCurrentElement();

				thingActionEvent.draw(pApplet);
			}

		}

	}

	@Override
	public void stop(World world, PApplet pApplet, ControlP5 controlP5) {
		started = false;
	}

	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setParameters(Thing thing) {
		// TODO Auto-generated method stub

	}

	@Override
	public void setup(PApplet pApplet) {
		// TODO Auto-generated method stub

	}

	@Override
	public void setup(PApplet pApplet, GuiFactory guiFactory) {

	}

	public void showSequenceList() {
		buttonList.setDrawable(true);
	}

	public void hideSequenceList() {
		buttonList.setDrawable(false);
	}

	@Override
	public void mouseWheelMoved(MouseWheelEvent e) {
		if (!doing)
			return;

		int rotation = e.getWheelRotation();
		if (rotation > 0) {
//			curr.previous();
			previousStep(true);
		} else if (rotation < 0) {
			
			nextStep(true);
		}
		 currentActionEvent = curr.getCurrentElement();
	}

	public ArrayListStepper<SequenceStep> getActionSequence() {
		return curr;
	}

	public void setActionSequence(
			ArrayListStepper<SequenceStep> arrayListStepper) {

		curr = arrayListStepper;
	}

	public XMLElement xmlIzation() {

		XMLElement seqsElement = new XMLElement();
		seqsElement.setName("Sequences");

		for (int index = 0; index < actionSequences.size(); index++) {
			ArrayListStepper<SequenceStep> sequence = actionSequences
					.get(index);

			XMLElement seqElement = new XMLElement();
			seqElement.setName("Sequence");
			seqElement.setAttribute("name", sequence.name);

			for (int index2 = 0; index2 < sequence.seqs.size(); index2++) {
				SequenceStep actionEvent = sequence.seqs.get(index2);

				XMLElement stepElement = actionEvent.xmlIzation();
				seqElement.addChild(stepElement);
			}

			seqsElement.addChild(seqElement);
		}
		return seqsElement;
	}

	public void desXmlIzation(XMLElement xmlElement) {

		for (XMLElement seqElement : xmlElement.getChildren()) {
			ArrayListStepper<SequenceStep> sequence = new ArrayListStepper<SequenceStep>();
			sequence.name = seqElement.getAttribute("name");
			for (XMLElement stepElement : seqElement.getChildren()) {
				SequenceStep thingActionEvent = new SequenceStep();

				thingActionEvent = thingActionEvent.desXmlIzation(stepElement);

				Set<Thing> things = thingActionEvent.actionEventMap.keySet();
				ArrayList<Thing> arrayList = new ArrayList<Thing>(things);

				// MEZCLAMOS CON LAS INSTANCIAS CONFIGURADAS

				for (Thing tempThing : arrayList) {
					Thing thing = world.findById(tempThing.getId());
					ActionEventList actionEventList = thingActionEvent.actionEventMap
							.get(tempThing);
					thingActionEvent.actionEventMap.remove(tempThing);
					thingActionEvent.actionEventMap.put(thing, actionEventList);

					for (int index = 0; index < actionEventList.size(); index++) {
						ActionEvent actionEvent = actionEventList.get(index);
						for (int index2 = 0; index2 < rendererManager.size(); index2++) {
							// ThingPainterDelegate delegate = rendererManager
							// .get(index2).;
							// if (delegate.getClass().equals(
							// actionEvent.painterDelegate.getClass())) {
							// actionEvent.painterDelegate = delegate;
							// }
						}
					}
				}

				sequence.seqs.add(thingActionEvent);
			}

			sequence.init();
			actionSequences.add(sequence);
			addToScrollList(sequence.name, sequence, actionSequences.size());
			curr = sequence;
			currentActionEvent = curr.getCurrentElement();
		}

	}

	@Override
	public void mouseMoved(int mouseX, int mouseY) {
		// TODO Auto-generated method stub
		Thing x = world.hovered(mouseX, mouseY);
		if (x != null) {
			// if(pApplet.getCursor().getType() == PApplet.ARROW)
			pApplet.cursor(PApplet.HAND);
		} else if (pApplet.getCursor().getType() == PApplet.HAND)
			pApplet.cursor(PApplet.ARROW);
	}

	public ArrayListStepper<SequenceStep> findSequenceByName(String name) {
		for (ArrayListStepper<SequenceStep> seq : actionSequences) {
			if (seq.name.equals(name)) {
				return seq;
			}
		}

		return null;
	}
}